package rpc

import (
	"fmt"
	"sync"
	"sync/atomic"

	"gitee.com/ameise84/e3dds/internal/log"
	"gitee.com/ameise84/e3dds/internal/message"
	net "gitee.com/ameise84/e3net"
	"gitee.com/ameise84/e3pool/go_pool"
	"gitee.com/ameise84/e3pool/obj_pool"
	"github.com/pkg/errors"
)

var (
	_gRawCtxPool obj_pool.Pool[*rawContext]
)

func init() {
	_gRawCtxPool = obj_pool.NewPool[*rawContext](func() *rawContext {
		return &rawContext{}
	})
}

func takeRawContext() *rawContext {
	return _gRawCtxPool.Get()
}

func freeRawContext(rc *rawContext) {
	go_pool.NewGoWorkerDo(nil, "free rpc raw context", func(...any) {
		rc.wg.Wait() //等待消息返回
		rc.c = nil
		_gRawCtxPool.Put(rc)
	})
}

// 代理连接
type rawContext struct {
	c           net.Conn
	lAddr       string
	rAddr       string
	isConnected atomic.Bool
	wg          sync.WaitGroup
}

func (ts *rawContext) E3LogMarshall() string {
	return fmt.Sprintf("rpc raw context[%v:%v]", ts.c.Tag(), ts.c.ID())
}

func (ts *rawContext) OnRecv(c net.Conn, b []byte) ([]byte, error) {
	msg := message.TakeMessage()
	err := msg.UnmarshalBinary(b)
	if err != nil {
		return nil, err
	}

	switch msg.GetKind() {
	case message.MsgNet:
		panic("rpc recv net msg")
	case message.MsgCall:
		msg.SetRspHandler(ts)
		ts.wg.Add(1)
	default:
	}
	rpcMsgPost(msg)
	return nil, nil
}

func (ts *rawContext) OnResponse(rsp message.Response) {
	msg := rsp.(*message.Msg)
	msg.CheckStepTime("rpc.rawCtx.OnResponse")
	err := ts.send(msg)
	ts.wg.Done()
	if err != nil {
		log.GetLogger().Warn("on response").Err(err).Object(ts).Println()
	}
}

func (ts *rawContext) Close() {
	//rpc 模块不可主动关闭连接
}

func (ts *rawContext) LocalAddr() string {
	return ts.lAddr
}

func (ts *rawContext) RemoteAddr() string {
	return ts.rAddr
}

func (ts *rawContext) send(msg *message.Msg) error {
	c := ts.c
	if !ts.isConnected.Load() {
		return errors.New("the rpc conn has been disconnected")
	}
	bs, _ := msg.MarshalBinary()
	return c.SendSync(bs)
}

func (ts *rawContext) onInit(c net.Conn) {
	ts.c = c
	ts.lAddr = c.LocalAddr()
	ts.rAddr = c.RemoteAddr()
	ts.isConnected.Store(true)
}

func (ts *rawContext) onClosed() {
	ts.isConnected.Store(false)
}
